-
-
Notifications
You must be signed in to change notification settings - Fork 79
[WIP] CycloneDX v2.0 Specification #652
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Steve Springett <[email protected]>
Syncing with master to incoporate v1.7 spec
...json/venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js
Fixed
Show fixed
Hide fixed
...json/venv/lib/python3.12/site-packages/urllib3/contrib/emscripten/emscripten_fetch_worker.js
Fixed
Show fixed
Hide fixed
This reverts commit bde33b2.
Signed-off-by: Steve Springett <[email protected]>
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
|
|
||
| // Verify paths exist | ||
| await fs.access(absoluteModelsDir); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
To fix the problem, we should validate that the directory supplied by the user (modelsDirectory) stays within an allowed root folder, or at least is not outside the current working directory if no stricter root is configured. The best approach for this CLI tool is to resolve the modelsDirectory input relative to the current working directory, normalize it with fs.realpathSync() or fs.promises.realpath, and then check that the resulting path is a subdirectory of a chosen safe root (for this CLI, the process's current working directory is reasonable). If the path escapes this root, the program should error out before continuing. The changes needed are:
- In the
bundleSchemasfunction, replace the logic on lines 143-149 to:- Normalize and resolve the modelsDirectory path, eliminating symbolic links.
- Check that the resolved directory is contained within the current working directory (or other configurable root, if needed).
- If not, throw an error or exit with a clear message.
- Add any imports necessary for this logic (none beyond those already present).
- These changes are all local to the code block shown—in
tools/src/main/js/bundle-schemas.js.
-
Copy modified lines R143-R148
| @@ -140,7 +140,12 @@ | ||
|
|
||
| async function bundleSchemas(modelsDirectory, rootSchemaPath, options = {}) { | ||
| try { | ||
| const absoluteModelsDir = path.resolve(modelsDirectory); | ||
| // Normalize and check modelsDirectory to avoid path traversal | ||
| const cwd = process.cwd(); | ||
| const absoluteModelsDir = await fs.realpath(path.resolve(modelsDirectory)); | ||
| if (!absoluteModelsDir.startsWith(cwd)) { | ||
| throw new Error(`Models directory must be within the current working directory: ${cwd}`); | ||
| } | ||
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
|
|
||
| // Verify paths exist |
|
|
||
| // Verify paths exist | ||
| await fs.access(absoluteModelsDir); | ||
| await fs.access(absoluteRootPath); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
To fix this issue, we need to ensure that user-supplied paths (modelsDirectory and rootSchemaPath) used by the tool are confined within an expected directory tree. Since the intended use is to process files in a specific models directory, it is safest to verify that the supplied rootSchemaPath resolves to a file inside modelsDirectory, and that both paths are normalized and checked for containment.
The best way to do this is:
- Normalize both paths using
path.resolveandfs.realpathSync(to resolve symlinks). - After normalization, check that
absoluteRootPathstarts withabsoluteModelsDir(possibly with path separator handling). - If the check fails, report an error to the user and halt execution, thus preventing access to unexpected resources.
Only make changes within the code region shown in tools/src/main/js/bundle-schemas.js, adding any required imports (e.g., using fs for realpathSync). Use synchronous calls for containment check before async access, as this avoids a TOCTOU race and keeps logic simple.
-
Copy modified line R5 -
Copy modified lines R147-R154 -
Copy modified lines R156-R161 -
Copy modified lines R163-R165 -
Copy modified lines R167-R172
| @@ -2,6 +2,7 @@ | ||
|
|
||
| const fs = require('fs').promises; | ||
| const path = require('path'); | ||
| const fsSync = require('fs'); | ||
|
|
||
| function isObject(value) { | ||
| return typeof value === 'object' && value !== null; | ||
| @@ -143,16 +144,32 @@ | ||
| const absoluteModelsDir = path.resolve(modelsDirectory); | ||
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
|
|
||
| // Verify paths exist | ||
| await fs.access(absoluteModelsDir); | ||
| await fs.access(absoluteRootPath); | ||
| // Canonicalize with realpathSync to resolve symlinks (synchronously to avoid TOCTOU) | ||
| let realModelsDir, realRootPath; | ||
| try { | ||
| realModelsDir = fsSync.realpathSync(absoluteModelsDir); | ||
| realRootPath = fsSync.realpathSync(absoluteRootPath); | ||
| } catch (err) { | ||
| throw new Error('Could not resolve one of the provided paths: ' + err.message); | ||
| } | ||
|
|
||
| const rootSchemaFilename = path.basename(absoluteRootPath); | ||
| const rootSchemaDir = path.dirname(absoluteRootPath); | ||
| // Ensure root schema file is inside the models directory | ||
| // Add path.sep to avoid partial prefix matches | ||
| const modelsRootPrefix = realModelsDir.endsWith(path.sep) ? realModelsDir : realModelsDir + path.sep; | ||
| if (!realRootPath.startsWith(modelsRootPrefix)) { | ||
| throw new Error(`The root schema path must be inside the models directory. Given: ${realRootPath} not under ${realModelsDir}`); | ||
| } | ||
|
|
||
| console.log(`Models directory: ${absoluteModelsDir}`); | ||
| console.log(`Root schema: ${absoluteRootPath}`); | ||
| // Verify paths exist (async access, after check) | ||
| await fs.access(realModelsDir); | ||
| await fs.access(realRootPath); | ||
|
|
||
| const rootSchemaFilename = path.basename(realRootPath); | ||
| const rootSchemaDir = path.dirname(realRootPath); | ||
|
|
||
| console.log(`Models directory: ${realModelsDir}`); | ||
| console.log(`Root schema: ${realRootPath}`); | ||
|
|
||
| // Generate output filenames | ||
| const baseFilename = rootSchemaFilename.replace('.schema.json', ''); | ||
| const bundledFilename = `${baseFilename}-bundled.schema.json`; |
| console.log(`Output: ${outputPath}\n`); | ||
|
|
||
| // Read all schema files in the models directory | ||
| const files = await fs.readdir(absoluteModelsDir); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
The best fix is to restrict the modelsDirectory parameter to a safe root directory. After resolving the provided path, check if the normalized, resolved path is contained within an intended root boundary (e.g., the current working directory or a specific, hardcoded directory). If the resolved path is outside this root, terminate the process with an error message. This can be achieved by:
- Defining a root directory (for example,
process.cwd()or an explicit path). - Using
path.resolve()to normalize both the root directory and user-provided directory. - Comparing them: ensuring that the user-specified directory begins with the normalized root directory path.
- Failing the operation if the check does not pass.
These changes are to be made in bundleSchemas, immediately after calculating the resolved path of modelsDirectory (line 143).
This fix requires no new methods but does require one additional code block for the path check.
-
Copy modified lines R144-R147
| @@ -141,6 +141,10 @@ | ||
| async function bundleSchemas(modelsDirectory, rootSchemaPath, options = {}) { | ||
| try { | ||
| const absoluteModelsDir = path.resolve(modelsDirectory); | ||
| const safeRootDir = path.resolve(process.cwd()); // restrict to working directory | ||
| if (!absoluteModelsDir.startsWith(safeRootDir + path.sep)) { | ||
| throw new Error(`Unsafe models directory: ${modelsDirectory}\nMust be within ${safeRootDir}`); | ||
| } | ||
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
|
|
||
| // Verify paths exist |
| const schemaPath = path.join(absoluteModelsDir, file); | ||
| console.log(` Reading ${file}...`); | ||
|
|
||
| const content = await fs.readFile(schemaPath, 'utf8'); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
|
|
||
| // Read the root schema | ||
| console.log(`\nReading root schema...`); | ||
| const rootContent = await fs.readFile(absoluteRootPath, 'utf8'); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
To fix the problem, we need to ensure that any file path provided by the user is validated or sanitized before it is used in file operations. Specifically, we should ensure that rootSchemaPath and any other user-supplied file paths are contained within a designated root folder (for example, the models directory or the current working directory). This is best accomplished by fully resolving the user input to an absolute path, then ensuring that it resides under a "safe" directory.
The fix will:
- Normalize user-supplied paths using
path.resolve. - Use
fs.realpathorfs.realpathSyncto account for symlinks. - Check that the absolute path starts with the safe root directory (e.g., the resolved models directory or the current working directory).
- Refuse to operate (error and exit) if the path is outside the allowable directory.
Changes will be made to tools/src/main/js/bundle-schemas.js, specifically in the CLI entry point and the beginning of the bundleSchemas function, to validate modelsDirectory and rootSchemaPath before attempting any file operations.
Additionally, since the code uses fs.promises, we'll use fs.realpath (async) for canonicalization.
-
Copy modified lines R143-R157
| @@ -140,8 +140,21 @@ | ||
|
|
||
| async function bundleSchemas(modelsDirectory, rootSchemaPath, options = {}) { | ||
| try { | ||
| const absoluteModelsDir = path.resolve(modelsDirectory); | ||
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
| // Canonicalize the directories | ||
| const absoluteModelsDir = await fs.realpath(path.resolve(modelsDirectory)); | ||
| const absoluteRootPathUnverified = path.resolve(rootSchemaPath); | ||
| // Canonicalize (resolve symlinks) | ||
| let absoluteRootPath; | ||
| try { | ||
| absoluteRootPath = await fs.realpath(absoluteRootPathUnverified); | ||
| } catch (e) { | ||
| throw new Error(`Root schema file does not exist: ${absoluteRootPathUnverified}`); | ||
| } | ||
| // Security: ensure root schema path is within modelsDirectory | ||
| // Or, less restrictively, within the process cwd; here we choose modelsDirectory | ||
| if (!absoluteRootPath.startsWith(absoluteModelsDir + path.sep)) { | ||
| throw new Error(`Root schema path (${absoluteRootPath}) is outside of models directory (${absoluteModelsDir})`); | ||
| } | ||
|
|
||
| // Verify paths exist | ||
| await fs.access(absoluteModelsDir); |
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
| // Write bundled (pretty) version | ||
| console.log('\nWriting bundled schema...'); | ||
| await fs.writeFile(bundledPath, JSON.stringify(finalSchema, null, 2)); | ||
| const bundledStats = await fs.stat(bundledPath); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
To fix this problem, user-supplied paths (namely, modelsDirectory and rootSchemaPath) must be validated to ensure that all constructed output files are written only inside a trusted root directory. The best way, as recommended, is to first resolve all user-supplied paths and then check that the resulting output paths (bundledPath, minifiedPath) reside inside the trusted models directory (or a stricter, predetermined directory). Specifically:
- Calculate the absolute paths for both input directory and schema file.
- When constructing the output path for the bundled and minified schemas, resolve them as absolute paths.
- Before writing, verify that the paths of the files to be written start with the absolute trusted directory path. Abort with an error if any path escapes the safe area.
- Add this validation before file writes in the method.
This fix applies only to the bundleSchemas function in tools/src/main/js/bundle-schemas.js. Changes required:
- Implement a function
isSubPath(parent, child)to check ifchildis withinparent. - Before any file writes (specifically
fs.writeFile(bundledPath, ...)andfs.writeFile(minifiedPath, ...)), check thatbundledPathandminifiedPathare subpaths ofabsoluteModelsDir. - Abort with a clear error if the check fails.
-
Copy modified lines R146-R152 -
Copy modified lines R171-R178 -
Copy modified lines R306-R309 -
Copy modified lines R324-R327
| @@ -143,6 +143,13 @@ | ||
| const absoluteModelsDir = path.resolve(modelsDirectory); | ||
| const absoluteRootPath = path.resolve(rootSchemaPath); | ||
|
|
||
| // Helper to check that child is within parent directory | ||
| function isSubPath(parent, child) { | ||
| const parentPath = path.resolve(parent) + path.sep; | ||
| const childPath = path.resolve(child); | ||
| return childPath.startsWith(parentPath); | ||
| } | ||
|
|
||
| // Verify paths exist | ||
| await fs.access(absoluteModelsDir); | ||
| await fs.access(absoluteRootPath); | ||
| @@ -161,6 +168,14 @@ | ||
| const bundledPath = path.join(rootSchemaDir, bundledFilename); | ||
| const minifiedPath = path.join(rootSchemaDir, minifiedFilename); | ||
|
|
||
| // Validate output paths are within models directory | ||
| if (!isSubPath(absoluteModelsDir, bundledPath)) { | ||
| throw new Error(`Refusing to write bundled schema outside models directory: ${bundledPath}`); | ||
| } | ||
| if (!isSubPath(absoluteModelsDir, minifiedPath)) { | ||
| throw new Error(`Refusing to write minified schema outside models directory: ${minifiedPath}`); | ||
| } | ||
|
|
||
| console.log(`Output (bundled): ${bundledPath}`); | ||
| console.log(`Output (minified): ${minifiedPath}\n`); | ||
|
|
||
| @@ -288,6 +303,10 @@ | ||
| // Write bundled (pretty) version | ||
| console.log('\nWriting bundled schema...'); | ||
| const prettyJson = JSON.stringify(finalSchema, null, 2); | ||
| // Double-check path before write | ||
| if (!isSubPath(absoluteModelsDir, bundledPath)) { | ||
| throw new Error(`Refusing to write bundled schema outside models directory: ${bundledPath}`); | ||
| } | ||
| await fs.writeFile(bundledPath, prettyJson); | ||
| const bundledStats = await fs.stat(bundledPath); | ||
| const bundledSizeKB = (bundledStats.size / 1024).toFixed(2); | ||
| @@ -302,6 +321,10 @@ | ||
| const lineCount = minifiedJson.split('\n').length; | ||
| console.log(` Minified JSON is on ${lineCount} line(s)`); | ||
|
|
||
| // Double-check path before write | ||
| if (!isSubPath(absoluteModelsDir, minifiedPath)) { | ||
| throw new Error(`Refusing to write minified schema outside models directory: ${minifiedPath}`); | ||
| } | ||
| await fs.writeFile(minifiedPath, minifiedJson); | ||
| const minifiedStats = await fs.stat(minifiedPath); | ||
| const minifiedSizeKB = (minifiedStats.size / 1024).toFixed(2); |
| // Write minified version | ||
| console.log('Writing minified schema...'); | ||
| await fs.writeFile(minifiedPath, JSON.stringify(finalSchema)); | ||
| const minifiedStats = await fs.stat(minifiedPath); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
The best and least intrusive fix is to ensure that any user-supplied path (modelsDirectory and/or rootSchemaPath) is validated before use, ensuring that the output files will only be created inside an intended safe directory (such as the models directory or another configured base root). After resolving the input paths to absolute paths, check that the output files (bundledPath, minifiedPath) are actually beneath the intended directory. If not, abort with a suitable error. This can be implemented by using path.resolve and then verifying that the output path starts with the intended root directory path. This fix should be applied in the bundleSchemas function, right after output paths are built but before files are written.
You need to:
- After constructing
bundledPathandminifiedPath, check that these are inside the expected output directory (likelyabsoluteModelsDiror, if required, a subdirectory of it). - If not, throw an error or exit.
- Optionally add a helper function for containment check.
- Import any necessary packages (though the standard library suffices here).
The edit is fully local to the snippet shown, within the definition of bundleSchemas.
-
Copy modified lines R164-R172
| @@ -161,6 +161,15 @@ | ||
| const bundledPath = path.join(rootSchemaDir, bundledFilename); | ||
| const minifiedPath = path.join(rootSchemaDir, minifiedFilename); | ||
|
|
||
| // Ensure output files are within the models directory (prevent path traversal) | ||
| function isSubPath(parent, child) { | ||
| const rel = path.relative(parent, child); | ||
| return !!rel && !rel.startsWith('..') && !path.isAbsolute(rel); | ||
| } | ||
| if (!isSubPath(absoluteModelsDir, bundledPath) || !isSubPath(absoluteModelsDir, minifiedPath)) { | ||
| throw new Error('Output path is outside the models directory. Refusing to write output files.'); | ||
| } | ||
|
|
||
| console.log(`Output (bundled): ${bundledPath}`); | ||
| console.log(`Output (minified): ${minifiedPath}\n`); | ||
|
|
Signed-off-by: Steve Springett <[email protected]>
| // Write bundled (pretty) version | ||
| console.log('\nWriting bundled schema...'); | ||
| const prettyJson = JSON.stringify(finalSchema, null, 2); | ||
| await fs.writeFile(bundledPath, prettyJson); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
| const lineCount = minifiedJson.split('\n').length; | ||
| console.log(` Minified JSON is on ${lineCount} line(s)`); | ||
|
|
||
| await fs.writeFile(minifiedPath, minifiedJson); |
Check failure
Code scanning / CodeQL
Uncontrolled data used in path expression High
user-provided value
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 1 day ago
To fix the problem, we must ensure that any user-supplied path does not "escape" a trusted base directory. This can be done by choosing a safe root directory (recommended to be the parent of the root schema), then verifying that all generated output paths (bundledPath, minifiedPath) are strictly located within this directory. To do this, after normalizing both the computed output path and the chosen root, we verify with path.relative() that the output path does not traverse above the root. If the output path would escape, block the operation and report an error.
- In
bundleSchemas(modelsDirectory, rootSchemaPath, options = {}), after computingrootSchemaDirand before producing output files, setsafeRoot = path.resolve(rootSchemaDir)and then, after generatingbundledPathandminifiedPath, check that both output files lie withinsafeRootbefore writing. - This can be done by ensuring that
path.relative(safeRoot, outputPath)does not start with ".." (or is not absolute). - If the check fails, throw an error or return an error response to block unsafe writes.
No new method definitions are needed, but the path validation logic should be added before any write operations.
-
Copy modified lines R156-R159 -
Copy modified lines R164-R165 -
Copy modified lines R167-R175
| @@ -153,17 +153,28 @@ | ||
| console.log(`Models directory: ${absoluteModelsDir}`); | ||
| console.log(`Root schema: ${absoluteRootPath}`); | ||
|
|
||
| // Generate output filenames | ||
| // Define a safe root directory for all output. Here, it's the directory containing the root schema. | ||
| const safeRoot = path.resolve(rootSchemaDir); | ||
|
|
||
| // Generate output filenames/paths | ||
| const baseFilename = rootSchemaFilename.replace('.schema.json', ''); | ||
| const bundledFilename = `${baseFilename}-bundled.schema.json`; | ||
| const minifiedFilename = `${baseFilename}-bundled.min.schema.json`; | ||
|
|
||
| const bundledPath = path.join(rootSchemaDir, bundledFilename); | ||
| const minifiedPath = path.join(rootSchemaDir, minifiedFilename); | ||
| const bundledPath = path.join(safeRoot, bundledFilename); | ||
| const minifiedPath = path.join(safeRoot, minifiedFilename); | ||
|
|
||
| // Validate that output paths are within safeRoot | ||
| function isContained(filePath, root) { | ||
| const rel = path.relative(root, path.resolve(filePath)); | ||
| return rel && !rel.startsWith('..') && !path.isAbsolute(rel); | ||
| } | ||
| if (!isContained(bundledPath, safeRoot) || !isContained(minifiedPath, safeRoot)) { | ||
| throw new Error('Refusing to write output files outside of the safe root directory'); | ||
| } | ||
|
|
||
| console.log(`Output (bundled): ${bundledPath}`); | ||
| console.log(`Output (minified): ${minifiedPath}\n`); | ||
|
|
||
| // Read all schema files in the models directory | ||
| const files = await fs.readdir(absoluteModelsDir); | ||
| const schemaFiles = files.filter(file => file.endsWith('.schema.json') && !file.includes('-bundled')); |
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
Signed-off-by: Steve Springett <[email protected]>
No description provided.